/* Includes ------------------------------------------------------------------*/
#include "serial.h"
#include "string.h"
#include "fcntl.h"
#include "unistd.h"
#include "stdlib.h"
#include "stdio.h"
#include "time.h"
// https://www.cnblogs.com/chengmin/p/3818133.html

/* Private macro -------------------------------------------------------------*/

/* Private typedef -----------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
void serial_receive(serial_t *serial);

/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
 * Function Name : serial_init
 * Description   : serial_init
 * Input         : [serial_t *serial, serial_config_t *config]
 *                 - serial: serial port
 *                 - config: serial port configuration
 * Output        : None
 * Return        : None
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
void serial_init(serial_t *serial, serial_config_t *config)
{
    // serial port name
    int port_len = strlen(config->port);
    serial->config.port = (char *)malloc(port_len + 1);
    memset(serial->config.port, 0, port_len + 1);
    memcpy(serial->config.port, config->port, port_len);

    // baudrate
    serial->config.baud = config->baud;
    serial->config.data_bits = config->data_bits;
    serial->config.stop_bits = config->stop_bits;
    serial->config.parity = config->parity;
    serial->config.flow_control = config->flow_control;
    serial->config.read_timeout = config->read_timeout;
    serial->config.rxbuffer_size = config->rxbuffer_size;
    serial->fd = -1;
    serial->rxhandler = NULL;
    // serial->rxbuffer = (byte *)malloc(serial->config.rxbuffer_size);
    // serial->rxlen = 0;
}

/*******************************************************************************
 * Function Name : serial_open
 * Description   : serial_open
 * Input         : [serial_t *serial]
 *                 - serial: serial port
 * Output        : None
 * Return        : None
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
void serial_open(serial_t *serial)
{
    serial->fd = open(serial->config.port, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial->fd < 0)
    {
        return;
    }
    struct termios tio;
    bzero(&tio, sizeof(tio));
    tio.c_cflag |= CLOCAL | CREAD;
    tio.c_cflag &= ~CSIZE;
    tio.c_cflag |= serial->config.data_bits;
    tio.c_cflag &= ~CSTOPB;
    tio.c_cflag |= serial->config.stop_bits;
    cfsetispeed(&tio, serial->config.baud);
    cfsetospeed(&tio, serial->config.baud);
    tio.c_cflag &= ~PARENB;
    if (serial->config.parity != SERIAL_PARITY_NONE)
    {
        tio.c_iflag |= INPCK | ISTRIP;
        tio.c_cflag |= serial->config.parity;
    }
    tio.c_cc[VTIME] = 100; // 100 * 100ms
    tio.c_cc[VMIN] = 100;
    tcflush(serial->fd, TCIFLUSH);
    tcsetattr(serial->fd, TCSANOW, &tio);
}

/*******************************************************************************
 * Function Name : serial_close
 * Description   : serial_close
 * Input         : [serial_t *serial]
 *                 - serial: serial port
 * Output        : None
 * Return        : None
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
void serial_close(serial_t *serial)
{
    close(serial->fd);
    serial->fd = -1;
}

/*******************************************************************************
 * Function Name : serial_write
 * Description   : serial_write
 * Input         : [serial_t *serial, byte *data, int len]
 *                 - serial: serial port
 *                 - data: data to be sent
 *                 - len: data length
 * Output        : None
 * Return        : int, number of bytes written
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
int serial_write(serial_t *serial, byte *data, int len)
{
    if (serial->fd < 0)
    {
        return -1;
    }
    tcflush(serial->fd, TCOFLUSH);
    int n = write(serial->fd, data, len);
    return n;
}

/*******************************************************************************
 * Function Name : serial_receive_handler_register
 * Description   : serial_receive_handler_register
 * Input         : [serial_t *serial, serial_rcv_handler_t handler]
 *                 - serial: serial port
 *                 - handler: receive handler
 * Output        : None
 * Return        : None
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
void serial_receive_handler_register(serial_t *serial, serial_rcv_handler_t handler)
{
    serial->rxhandler = handler;
    pthread_create(&serial->thread, NULL, (void *)serial_receive, serial);
}

/*******************************************************************************
 * Function Name : serial_receive
 * Description   : serial_receive
 * Input         : [serial_t *serial]
 *                 - serial: serial port
 * Output        : None
 * Return        : None
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
void serial_receive(serial_t *serial)
{
    if (serial->fd < 0)
    {
        return;
    }
    byte *buf = (byte *)malloc(serial->config.rxbuffer_size);
    int index = 0;
    uint32_t tick = sys_run_ms();
    while (1)
    {
        int n = read(serial->fd, buf + index, serial->config.rxbuffer_size);
        if (n > 0)
        {
            index += n;
            tick = sys_run_ms();
        }
        else
        {
            if (sys_run_ms() - tick > serial->config.read_timeout && index > 0)
            {
                if (serial->rxhandler != NULL)
                {
                    serial->rxhandler(buf, index);
                }
                index = 0;
            }
        }
        // tcflush(serial->fd, TCIFLUSH);
        usleep(1000);
    }
    free(buf);
}

/*******************************************************************************
 * Function Name : sys_run_ms
 * Description   : sys_run_ms
 * Input         : NULL
 * Output        : tick, unit: ms
 * Return        : uint32_t
 * Notes         : None
 * Attention	 : None
 *******************************************************************************/
uint32_t sys_run_ms(void)
{
    struct timespec ts = {0};
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
/************************ (C) COPYRIGHT PURESUN **** END OF FILE ***************/
